本系列文章已重新編修,並在加入部分 ES6 新篇章後集結成書,有興趣的朋友可至天瓏書局選購,感謝大家支持。
購書連結 https://www.tenlong.com.tw/products/9789864344130
讓我們再次重新認識 JavaScript!
現在人們談論到 SPA (Single Page Application, 單頁式應用) 可能都會認為是在 2007 年 iPhone 推出之後,Mobile Web 日漸增長之後才開始蓬勃發展的。
事實上,SPA 的崛起可能比起你我的想像還要早得許多。
最早期的網頁幾乎都是以靜態網頁為主,所有資料直接從 Server 端輸出,幾乎不需要做運算,就是單純的 HTML。就算有後端語言,最多也就是負責表單的處理。 後來過了幾年,有了「動態網頁程式」的語言 (如 PHP、ASP 等) 開始由後端程式語言負責處理頁面邏輯,加上資料庫系統的成熟,使得原本無法紀錄狀態的網頁,可以利用資料庫來記錄狀態及資料。 人們這才開始意會到,原來網頁是可以用來開發「應用程式」的。
上面講的是後端程式的部分,同一時間另一個戰場在使用者端。
除了日漸成熟的後端技術外,在當時網頁標準還沒有底定的同時,各家廠商為了搶佔市場,也紛紛推出各種增強或擴充瀏覽器的應用程式,像是早期的 Java Applet、微軟主打的 ActiveX,都可以算是後來 Rich Internet Application (RIA) 的始源。
時間來到 2004 年。 幾乎所有在介紹 Ajax 的文章都會提到這個產品。
沒錯,就是由 Google 所推出的 Gmail。
其實 Ajax 光看名字就知道,「Asynchorous Javascript And XML」在當時並不是什麼特別新的技術,而是把好幾項本來就有的東西組合起來,但這項技術確實為後來 SPA 的發展奠定重要基礎。
除了 Ajax 以外,當時想要在網頁上製作應用程式還有另外一派,那就是由 Macromedia 所開發的 Flash (後來被 Adobe 買下) 以及微軟的 Sliverlight。
然而,不管是透過外掛在瀏覽器的 RIA 應用,或者是只採用 CSS、HTML、Javascript 來控制 HTML DOM 的 Dynamic HTML (Ajax) ,我們都可以從中發現共通點,網頁的應用實際上已經開始漸漸由「後端」轉移到「前端」(更精準一點應該稱 Client 端) 了。
圖片來源:ASP.NET - Single-Page Applications: Build Modern, Responsive Web Apps with ASP.NET
傳統多頁式的網站與單頁式網站最大的差別就是省下了換頁的成本,不需要重新載入網頁,操作的回饋更即時。 取而代之的是透過 Ajax 取得需要被更新的資料,然後透過 JavaScript 將網頁的內容替換。 但同時也遇到了新的挑戰,首先面臨的問題就是使用者的習慣、狀態的管理,以及 SEO 的考量。
先前提過,長久以來使用者「被教育」操作網頁建置的系統時,要回到前一個步驟,一定會去按「上一頁」,但網頁是用 Ajax 更新的資料,哪裡來的上一頁與下一頁可以給使用者切換。
當然早期前輩們也想過這個問題,想要動 URL 又不能換頁,只好改 URL Hash。
window.location.hash = "#main_page";
window.location.hash = "#content_page";
window.location.hash = "#list_page";
然後搭配 hashchange
事件,去監聽 URL Hash 的狀態來決定目前顯示的內容。
這種方式算是最早的前端路由解決方案。
還好後來 HTML5 之後,新的規範提供了 History API,可以透過 pushState()
、replaceState()
的方式更新 URL,以及 history.go();
、 history.back();
來切換頁面的上下頁路徑,同時也提供了 state
物件來讓開發者存取每一頁的狀態。
而這種搭配 pushState
的方式,甚至有人把它與 Ajax 合併,合稱為「Pjax」。
另一個需要面臨的問題是 SEO,雖然 Google 曾宣稱可以看懂 Javascript 渲染的結果,但是...
...有些事情還是聽聽就好。 況且搜尋引擎也不止 Google 一家。
在處理這類問題最大的挑戰是,前後端需要各自實作路由來處理 url 變化,前後端分別維護各自的 view 及 routing,且要能互相對應,換言之就是後端要處理 API 的資料,同時也要負責在使用者 landing 的時候提供畫面的渲染。 而前端負責的是,當後端將第一頁畫面渲染之後,就得接手使用者的後續操作。
這樣的解決方式就算是機器人來爬也絕對沒有問題,但等於開發人員需要做兩份工,專案複雜度也是倍數上升。
是不是有必要做到這樣其實見仁見智,規劃專案的時候,有時候除了「潮」也要考慮到開發的成本與未來的維護性。
另一個使 SPA 快速發展的原因,則是我們一開始提到的行動裝置的快速成長。
在現在這個幾乎人手一機的時代,有些人家裡可能沒有電腦,但幾乎不會沒有手機或是平板。
在過去頻寬不像現在有 4G 這麼快速的年代,比起傳統的換頁式網站,行動版的網頁採用 Ajax 取得 JSON 來更新資料除了節省頻寬的好處外,畫面的更新回饋也更加即時。
但需要注意的是,SPA 並不是單純把一堆頁面全部往同一頁塞就好。
大約六、七年前,有個前端框架叫 PhoneGap (現更名為 Cordova),它最大的賣點就是讓開發人員得以使用 HTML5、JavaScript、CSS 等 web 標準技術來開發原生程式,其實說穿了就是將 Web 包裝成 App,透過 SDK 提供的 Bridge 可以使網頁應用去呼叫裝置的功能。
但由於這樣方式開發出來的 App 通常都還有「網頁」的思維,也常聽到開發者送審時被 Apple 打槍的結果。
所以說除了技術面以外,開發 SPA 的另一個重點則是開發者的思維,否則技術再先進,做出來的東西依然是網頁,那個樣子怎麼看都不會是應用程式。
其實現代主流的前端框架,早一點的像 Backbone、AngularJS,到後來的 React、Vue 也有提供 SPA 相關的解決方案。 然而了解哪些場景該用傳統後端,哪些該用 SPA 來開發,則是 Web 開發人員的未來需要面對的另一個課題。
網頁開發的思維從早期的「義大利麵式程式碼」(Spaghetti code) 把所有的東西通通往 HTML 頁面塞,到了後來有人提倡「關注點分離」,將 HTML、CSS 及 JavaScript 拆開來,這是表現層級上的關注點分離。
然而,當專案的架構越來越大,人們開始把「關注點」從表現層移到了架構層面。 於是,分成了「資料層」、「表現層」以及「邏輯層」,也就是所謂的 MVC 概念。
當瀏覽器開啟 View 的時候,View 會向 Controller 提出請求。 Controller 處理完商業邏輯的部分之後,要求 Model 端改變狀態,然後 Model 再將資料反映到 View 上。
以 Backbone 來說,使用者可以透過 DOM 事件對 View 發送指令,再由 View 要求 Model 改變狀態。 但同時使用者也可以透過操作 URL 來對 Controller 做操作,再由 Controller 去改變 View。
這個時候你會發現前端 MVC 與後端 MVC 有著一個很大的差異: 前端的 MVC 著重在事件流程,而後端的重點在於資料流。
後端 MVC 的 View 就相當於是前端的全部了:
來源:http://slides.com/evenchange4/2014-ntuim-prepconf#/2/3
當前後端分離後,後端幾乎不需要去處理 View 的部分。相對地,後端的 V 變成前端的 M,那些原本放在後端處理畫面、邏輯的部分,其實很大程度地往前端移動。
於是前端框架慢慢發展成 MVVM 模式。
像比較早期的 Ember、Angular 採用資料的雙向綁定作法,當 View 操作 (DOM event) 變動,透過 ViewModel 去操作 Model (JavaScript Object),反過來也是。
後來的 React、Vue 也都延續採用 Single Source of Truth (SSOT) 的準則,使得資料流更容易被追蹤,架構也更好維護。
一般來說,後端的 Controller 是指 URL 與 Http Method 的部分,而前端可以透過 URL 操作 (hashtag / pushState) 以及 DOM Event 去對 View 做操作。 所以通常前端框架不會直接稱自己是 MVC 框架,而是被稱作 MV* (MVC/MVP/MVVM...) 的框架。
今天講了滿多與 SPA 相關的東西,也是藉此與各位聊聊網頁應用的架構,是如何從傳統的後端為主逐漸發展至 SPA (但目前也未必是主流),以及它所延伸出的問題。
那麼下回我們就延伸這個話題,繼續與大家聊聊,在 MV* 框架出現後, Web Component 為什麼漸漸變成主流,以及前端開發生態圈的發展。
前端架构大家都挂在嘴边,vue、react大家都在用,但是好像又都不懂。
kuro大大有机会开一个前端架构的主题嘛?